/*
** Mortem's HGE Addon 1.1
** Copyright (C) 2006-2007, Osokin 'elmortem' Makar
** http://elmortem.mirgames.ru
**
** Vector class.
*/

#ifndef _MHAVECTOR_H_
#define _MHAVECTOR_H_

#define _USE_MATH_DEFINES
#include <hge.h>
#include <math.h>

enum {
	MHAVECTOR_LEFT, 
	MHAVECTOR_RIGHT,
	MHAVECTOR_BEYOND,
	MHAVECTOR_BEHIND,
	MHAVECTOR_BETWEEN,
	MHAVECTOR_ORIGIN,
	MHAVECTOR_DESTINATION
};

class mhaVector {
public:
	float x, y;

	mhaVector() {
		this->x = 0.f;
		this->y = 0.f;
	}

	mhaVector(float x, float y) {
		this->x = x;
		this->y = y;
	}

	mhaVector(const mhaVector &v) {
		this->x = v.x;
		this->y = v.y;
	}
	
	mhaVector	operator- (const mhaVector &v) const { return mhaVector(x-v.x, y-v.y); }
	mhaVector	operator+ (const mhaVector &v) const { return mhaVector(x+v.x, y+v.y); }
	mhaVector	operator+ (const float &f) const { return mhaVector(x+f, y+f); }
	mhaVector	operator* (float scalar) const { return mhaVector(x*scalar, y*scalar); }
	mhaVector operator/ (float scalar) const { return mhaVector(x/scalar, y/scalar); }

	mhaVector&	operator-= (const mhaVector &v) { x-=v.x; y-=v.y; return *this; }
	mhaVector&	operator+= (const mhaVector &v) { x+=v.x; y+=v.y; return *this; }
	mhaVector&	operator*= (const mhaVector &v) { x*=v.x; y*=v.y; return *this; }
	mhaVector&	operator*= (float scalar) { x*=scalar; y*=scalar; return *this; }
	mhaVector	operator- () { return mhaVector(-x, -y); }
	bool		operator== (const mhaVector &v) const { return (x==v.x && y==v.y); }
	bool		operator!= (const mhaVector &v) const { return (x!=v.x || y!=v.y); }
	mhaVector& operator = (const mhaVector &v) { this->x = v.x; this->y = v.y; return *this; }


	/*bool operator == (const mhaVector &v) {
		return (this->x == v.x && this->y == v.y) ? true : false;
	}

	bool operator != (const mhaVector &v) {
		return (this->x != v.x || this->y != v.y) ? true : false;
	}

	mhaVector& operator = (const mhaVector &v) {
		this->x = v.x; this->y = v.y;
		return *this;
	}

	mhaVector& operator - (const mhaVector &v) {
		this->x = this->x - v.x;
		this->y = this->y - v.y;
		return *this;
	}
	
	mhaVector& operator - (const float &f) {
    this->x = this->x - f;
		this->y = this->y - f;
		return *this;
	}

	mhaVector& operator + (const mhaVector &v) {
		this->x = this->x + v.x;
		this->y = this->y + v.y;
		return *this;
	}

	mhaVector& operator * (const float &f) {
		this->x = this->x * f;
		this->y = this->y * f;
		return *this;
	}*/

	void Set(float x, float y) {
		this->x = x;
		this->y = y;
	}
	
	void Add(float x, float y) {
    this->x += x;
    this->y += y;
  }

	void Move(float dist, float angle) {
		angle = M_PI / 180 * angle;
		this->x = this->x - sinf(angle) * dist;
		this->y = this->y - cosf(angle) * dist;
	}

	void Move2(float dist, float angle) {
		this->x = this->x - sinf(angle) * dist;
		this->y = this->y - cosf(angle) * dist;
	}
	
	void MoveTo(float dist, mhaVector to, bool stop = true) {
    float dst = this->DistanceTo(to);
    if(dst < dist && stop) {
      this->x = to.x; this->y = to.y;
    } else {
      this->Move(dist, this->AngleTo(to));      
    }
  }

	void Rotate(mhaVector center, float angle) {
		angle = M_PI / 180 * angle;
		this->x = (this->x - center.x) * cosf(angle) - (this->y - center.y) * sinf(angle) + center.x;
		this->y = (this->y - center.y) * cosf(angle) + (this->x - center.x) * sinf(angle) + center.y;
	}

	float AngleTo(mhaVector to) {
		if(x == to.x && y == to.y) return -1.f;

		float angle = atanf((to.y - y) / (to.x - x)) * 180 / M_PI;
		if(to.x >= x)
			return 180.f - angle + 90.f;
		else
			return 180.f - angle - 90.f;		
		
		if(to.x >= x)
			return angle + 90.f;
		else
			return angle - 90.f;
	}

	float Angle(void) {
		if(x == 0 && y == 0) return 0;

		float angle = atanf(y / x) * 180 / M_PI;
		if(0 <= x)
			return 180.f - angle + 90.f;
		else
			return 180.f - angle - 90.f;		
		
		if(0 >= x)
			return angle + 90.f;
		else
			return angle - 90.f;
	}

	float DistanceTo(mhaVector to) {
		return sqrtf((to.x - this->x) * (to.x - this->x) + (to.y - this->y) * (to.y - this->y));
	}

	float Distance(void) {
		return sqrtf(this->x * this->x + this->y * this->y);
	}

	mhaVector FromLine(mhaVector p1, mhaVector p2) {
		float d, d1, d2;
  
		d1 = this->DistanceTo(p1);
		d2 = this->DistanceTo(p2);
  
		d = d1 / ((d1 + d2) / 100);
		d = p1.DistanceTo(p2) / 100 * d;
		p1.Move(d, p1.AngleTo(p2));

		return p1;
	}

	mhaVector Abs(void) {
		if(x < 0) x = -x;
		if(y < 0) y = -y;
		return *this;
	}
	
	void f(mhaVector p) {
		this->x = (this->x + p.x) / 2;
		this->y = (this->y + p.y) / 2;
	}

	float Dot(const mhaVector v) const {
		return x*v.x + y*v.y;
	}
	float Length() const {
		return sqrtf(Dot(*this));
	}
	void Clamp(const float max) {
		if(Length() > max) {
			Normalize();
			x *= max;
			y *= max;
		}
	}
	mhaVector Normalize(void) {
		float dot = Dot(*this);
		if(dot != 0.f) {
			float rc = 1.f/sqrtf(dot);
			x *= rc;
			y *= rc;
		}
		return *this;
	}
	int classify(mhaVector p0, mhaVector p1) {
		mhaVector p2 = *this;
		mhaVector a = p1 - p0;
		mhaVector b = p2 - p0;
		float sa = a.x * b.y - b.x * a.y; //a.x*b.x + a.y*b.y;
		if(sa > 0.f) return MHAVECTOR_LEFT;
		if(sa < 0.f) return MHAVECTOR_RIGHT;
		if((a.x * b.x < 0.f) || (a.y * b.y < 0.f)) return MHAVECTOR_BEHIND;
		if(a.Length() < b.Length()) return MHAVECTOR_BEYOND;
		if(p0 == p2) return MHAVECTOR_ORIGIN;
		if(p1 == p2) return MHAVECTOR_DESTINATION;
		return MHAVECTOR_BETWEEN;
	}
};

inline mhaVector operator* (const float s, const mhaVector &v)		{ return v*s; }
//inline float operator^ (const mhaVector &v, const mhaVector &u) { return v.Angle2(&u); }
inline float operator% (const mhaVector &v, const mhaVector &u) { return v.Dot(u); }

#endif
